﻿using ElasticSearchTest.Model;
using Nest;
using NPOI.POIFS.Crypt.Dsig;
using NPOIHelper;
using PMS.Search.Domain.Entities;
using PMS.Search.Elasticsearch;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ElasticSearchTest
{
    public class HistoryEsService
    {
        private readonly EsClient _client;
        private readonly EsIndexConfig.Index _index = EsIndexConfig.Instance.HistoryIndex;

        public HistoryEsService(EsClient client)
        {
            _client = client;
        }
        public void Test()
        {
            //var resp = CreateIndex();

            Guid? userId = null;
            var pageIndex = 1;
            var pageSize = 9999999;
            IEnumerable<SearchHistory> histories = new List<SearchHistory>();

            SearchGroupCountExcel();

            //SearchExcel();
        }

        private void SearchExcel()
        {
            var data = Search("", 1, 500000); // es max 500000

            var helper = NPOIHelperBuild.GetHelper("搜索历史记录");
            helper.Add("sheet1", data, new Column[] {
                new Column("SearchWord", "关键字")
            });
            var fileName = helper.FullName;
            //var fileName = "D://" + helper.FullName;

            try
            {
                helper.ReportClient(fileName);
            }
            catch (Exception)
            {
            }
        }

        public void SearchGroupCountExcel()
        {
            int size = 500000;
            List<GroupCount> data = SearchGroupCount(size); // es max 500000
            var helper = NPOIHelperBuild.GetHelper("搜索历史记录和次数");
            helper.Add("sheet1", data, new Column[] {
                new Column("SearchWord", "关键字"),
                new Column("TotalCount", "搜索次数", ColumnType.Number),
            });

            var fileName = helper.FullName;
            try
            {
                helper.ReportClient(fileName);
            }
            catch (Exception)
            {
            }
        }

        /// <summary>
        /// {Type: too_many_buckets_exception Reason: "Trying to create too many buckets.
        /// Must be less than or equal to: [10000] but was [10001]. 
        /// This limit can be set by changing the [search.max_buckets] cluster level setting."}
        /// </summary>
        /// <param name="size"></param>
        /// <returns></returns>
        public List<GroupCount> SearchGroupCount(int size)
        {
            var search = new SearchDescriptor<SearchHistory>()
                .Index(new string[] { _index.SearchIndex })
                .Size(0);

            var mustQuery = new List<Func<QueryContainerDescriptor<SearchHistory>, QueryContainer>>()
            {
            };

            search
                .TrackTotalHits()
                .Query(q => q.Bool(b => b.Must(mustQuery)))
                .Aggregations(agg =>
                    agg.Terms("group", t =>
                        t.Field("searchWord.keyword")
                        .Size(size)
                  ));


            var result = _client.GetClient().Search<SearchHistory>(search);
            var total = result.Total;

            var groupAgg = result.Aggregations.Terms("group");

            var list = new List<GroupCount>();
            foreach (KeyedBucket<string> item in groupAgg.Buckets)
            {
                list.Add(new GroupCount() { 
                    SearchWord = item.Key,
                    TotalCount = item.DocCount
                });
            }
            return list;
        }

        public List<SearchHistory> Search(string keyword, int pageNo = 1, int pageSize = 10)
        {
            int from = (pageNo - 1) * pageSize;
            int size = pageSize;
            var search = new SearchDescriptor<SearchHistory>()
                .Index(new string[] { _index.SearchIndex })
                .From(from)
                .Size(size);

            var mustQuery = new List<Func<QueryContainerDescriptor<SearchHistory>, QueryContainer>>()
            {
            };
            var constantQuery = new ConstantScoreQuery();

            #region mustQuery
            //模糊查询
            if (!string.IsNullOrWhiteSpace(keyword))
            {
                var query = new MultiMatchQuery()
                {
                    Fields = Infer.Field("name.pinyin^1.5").And("userName.pinyin"),
                    Query = keyword
                };
                mustQuery.Add(q => query);
            }

            #endregion
            search.Collapse(c => c.Field("searchWord.keyword"))
                .Query(q => q.Bool(b => b.Must(mustQuery)));


            var result = _client.GetClient().Search<SearchHistory>(search);
            var total = result.Total;
            return result.Hits.Select(s => s.Source).ToList();
        }

        public CreateIndexResponse CreateIndex()
        {
            var resp = _client.GetClient().Indices.Delete(_index.Name);
            var createIndexResponse = _client.GetClient().Indices.Create(_index.Name, c => c
               .Aliases(ad => ad.Alias(_index.Alias))
               .Settings(s => s
                   .Setting("max_ngram_diff", 20)
                   .Setting("max_result_window", 500000)
                   .Analysis(a => a
                       .CharFilters(cf => cf
                           .UserDefined("tsconvert", new TSConvertCharFilter("t2s", "#"))
                        )
                       .TokenFilters(tf => tf
                           .UserDefined("pinyin_max_filter",
                               new PinyinTokenFilter
                               {
                                   KeepSeparateFirstLetter = false,
                                   KeepJoinedFullPinyin = true,
                                   KeepFullPinyin = true,
                                   KeepOriginal = true,
                                   LimitFirstLetterLength = 16,
                                   Lowercase = true,
                                   RemoveDuplicatedTerm = true
                               })
                           .UserDefined("pinyin_filter",
                               new PinyinTokenFilter
                               {
                                   KeepFirstLetter = false,
                                   KeepSeparateFirstLetter = false,
                                   KeepJoinedFullPinyin = true,
                                   KeepFullPinyin = false,
                                   KeepOriginal = true,
                                   LimitFirstLetterLength = 16,
                                   Lowercase = true,
                                   RemoveDuplicatedTerm = true
                               })
                       )
                       .Analyzers(aa => aa
                           .Custom("string_lowercase", ca => ca
                                .Tokenizer("keyword")
                                .Filters("lowercase")
                            )
                           .Custom("ik_analyzer", ca => ca
                                .CharFilters("tsconvert")
                                .Tokenizer("ik_max_word")
                                .Filters("pinyin_max_filter", "lowercase")
                            )
                           .Custom("ik_search_analyzer", ca => ca
                                .CharFilters("tsconvert")
                                .Tokenizer("ik_smart")
                                .Filters("lowercase")
                            )
                           .Custom("ik_pinyin_analyzer", ca => ca
                                .CharFilters("tsconvert")
                                .Tokenizer("ik_max_word")
                                .Filters("pinyin_max_filter", "lowercase")
                            )
                           .Custom("ik_pinyin_search_analyzer", ca => ca
                                .CharFilters("tsconvert")
                                .Tokenizer("ik_smart")
                                .Filters("pinyin_filter", "lowercase")
                            )
                       )
               ))
               .Map<SearchHistory>(mm => mm
              .Dynamic(false)
              .Properties(p => p
                    .Text(t => t
                        .Name(n => n.SearchWord)
                        .Fields(f =>
                            f.Text(ft => ft.Name("pinyin")
                                .Store(false)
                                .TermVector(TermVectorOption.WithPositionsOffsets)
                                .Analyzer("ik_pinyin_analyzer")
                                .SearchAnalyzer("ik_analyzer")
                                .Boost(5))
                            .Keyword(ft => ft.Name("keyword"))
                        )
                    )
                    .Keyword(t => t
                        .Name(n => n.Word)
                    )
                    .Number(t => t.Name(n => n.CityCode))
                    .Date(t => t.Name(n => n.DateTime))
                    .Keyword(t => t
                        .Name(n => n.UserId)
                    )
                    .Keyword(t => t
                        .Name(n => n.UUID)
                    )
                    .Keyword(t => t
                        .Name(n => n.Application)
                    )
                    .Keyword(t => t
                        .Name(n => n.Channel)
                    )
                ))
            );
            return createIndexResponse;
        }
    }
}
